/*

This file is part of Rooms3D.

Copyright (C) 2008 Pixecur Technologies Incorporated (Esotera).
Visit Esotera at http://www.esoteras3.com
 
Rooms3D is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
 
Rooms3D is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with Rooms3D; see the file COPYING.  If not, write to 
Pixecur Technologies, 213-31 Peet Street, St. John's, NL, A1B 3W8. 
 
Linking this library statically or dynamically with other modules is
making a combined work based on this library.  Thus, the terms and
conditions of the GNU General Public License cover the whole
combination.
 
 */
package com.datasentinel.rooms.file;

import java.awt.Graphics;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.security.NoSuchAlgorithmException;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.imageio.ImageIO;

import com.datasentinel.file.FileBuilder;
import com.datasentinel.file.FileBuilderFactory;
import com.datasentinel.util.DigestUtils;

/**
 * @author rickp
 * 
 */
public class FileThumbnailStore
{
	private static Logger logger = Logger.getLogger(FileThumbnailStore.class.getName());
	private static DigestUtils digestUtils;
	private final Object storeLock = new Object();
	private static FileBuilder fileBuilder = FileBuilderFactory.createFileBuilder();
	
	static
	{
		try
		{
			digestUtils = new DigestUtils("MD5");
		}
		catch (NoSuchAlgorithmException e)
		{
			logger.log(Level.SEVERE, "error creating MD5 digester", e);
			throw new RuntimeException(e);
		}
	}

	private String getThumbnailFilename(File file, boolean legacy)
	{
		try
		{
			String fileSpec = file.toURI().toURL().toString();
			if (legacy)
			{
				int index = fileSpec.indexOf('/', 5);
				if (index > 0)   // old style 'embedded drive' names were of form "px:/drive:filespec" but are now "px:/drive/filespec"
					fileSpec = fileSpec.substring(0, index) + ":" + fileSpec.substring(index + 1);
			}
			return digestUtils.sum(fileSpec).toString() + ".png";
		}
		catch (MalformedURLException e)
		{
			throw new RuntimeException(e);
		}
	}
	
	private File getThumbnailsFolder(File file) throws IOException
	{
		return buildThumbnailsFolder(fileBuilder.getRoot(file));
	}

	private File getDefaultThumbnailsFolder() throws IOException
	{
		return buildThumbnailsFolder(fileBuilder.getDefaultRoot());
	}
	
	private File buildThumbnailsFolder(File root)
	{
		File thumbnailsFolder;
		try
		{
			File rooms3dFolder = fileBuilder.build(root, FileConstants.ROOMS3D_FOLDER);
			thumbnailsFolder = fileBuilder.build(rooms3dFolder, FileConstants.THUMBNAILS_FOLDER);
		}
		catch (IOException e1)
		{
			try
			{
				File userHome = fileBuilder.build(fileBuilder.build(System.getProperty("user.home")), "");
				File rooms3dFolder = fileBuilder.build(userHome, FileConstants.ROOMS3D_FOLDER);
				thumbnailsFolder = fileBuilder.build(rooms3dFolder, FileConstants.THUMBNAILS_FOLDER);
			}
			catch (IOException e2)
			{
				throw new RuntimeException(e2);
			}
		}
		if (!thumbnailsFolder.exists() && thumbnailsFolder.canWrite())
		{
			thumbnailsFolder.mkdirs();
		}
		return thumbnailsFolder;
	}

	private File getThumbnailFile(File thatMightBeThumbnailed) throws IOException
	{
		synchronized (storeLock)
		{
			for (int i = 0; i < 2; i++)
			{
				/*
				 * Iterate twice to try the newer and older versions of the thumbnail URI string
				 */
				String thumbnailFile = getThumbnailFilename(thatMightBeThumbnailed, i == 1);
				File thumbnailsFolder;
				try
				{
					thumbnailsFolder = getThumbnailsFolder(thatMightBeThumbnailed);
				}
				catch (IllegalArgumentException e)
				{
					/*
					 * thatMightBeThumbnailed is a local file spec from a drag operation
					 * Need to create a unique name for this temp file that won't collide
					 * with existing thumbnail files in user's home 
					 * We use the full local file spec for this.
					 */
					thumbnailsFolder = getDefaultThumbnailsFolder();
					thumbnailFile = thatMightBeThumbnailed.getAbsolutePath();
				}
				if (fileBuilder.exists(thumbnailsFolder, thumbnailFile))
				{
					return fileBuilder.build(thumbnailsFolder, thumbnailFile);
				}
				thumbnailsFolder = getDefaultThumbnailsFolder();
				if (fileBuilder.exists(thumbnailsFolder, thumbnailFile))
				{
					return fileBuilder.build(thumbnailsFolder, thumbnailFile);
				}
			}
			return null;
		}
	}
	public boolean isThumbnailed(File thatMightBeThumbnailed)
	{
		try
		{
			File thumbnail = getThumbnailFile(thatMightBeThumbnailed);
			return thumbnail != null && thumbnail.lastModified() > thatMightBeThumbnailed.lastModified();
		}
		catch (Exception e)
		{
			return false;
		}
	}

	public void removeThumbnailImageForFile(File thatWasThumbnailed) throws IOException
	{
		synchronized (storeLock)
		{
			File thumbnailFile = getThumbnailFile(thatWasThumbnailed);
			if (thumbnailFile != null)
			{
				logger.finer(String.format("removing thumbnail [%s] for file [%s]",	thumbnailFile, thatWasThumbnailed));
				if (!fileBuilder.delete(thumbnailFile))
					throw new IOException("could not delete file: " + thumbnailFile);
			}
		}
	}

	public Image loadThumbnailImageForFile(File thatWasThumbnailed) throws IOException
	{
		synchronized (storeLock)
		{
			File thumbnailFile = getThumbnailFile(thatWasThumbnailed);
			if (thumbnailFile == null)
			{
				throw new IOException("Thumbnail file does not exist");
			}
			logger.finer(String.format("loading stored thumbnail [%s] for file [%s]",
					thumbnailFile, thatWasThumbnailed));
			ByteArrayOutputStream bos = new ByteArrayOutputStream();
			InputStream is = fileBuilder.buildFileInputStream(thumbnailFile);
			byte[] buf = new byte[8192];
			int numRead = 0;
			while (-1 != (numRead = is.read(buf)))
			{
				bos.write(buf, 0, numRead);
			}
			is.close();
			ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
			return ImageIO.read(bis);
		}
	}

	public void saveThumbnailImageForFile(File toThumbnail, Image thumbnailImage) throws IOException
	{
		synchronized (storeLock)
		{
			String thumbnailFilename = getThumbnailFilename(toThumbnail, false);
			File thumbnailFile = fileBuilder.build(getThumbnailsFolder(toThumbnail), thumbnailFilename);
			if (!thumbnailFile.canWrite())
				thumbnailFile = fileBuilder.build(getDefaultThumbnailsFolder(), thumbnailFilename);
			logger.finer(String.format("saving thumbnail [%s] for file [%s]",
					thumbnailFile, toThumbnail));
			RenderedImage thumbnailToWrite = null;
			if (!thumbnailFile.exists())
			{
				thumbnailFile.createNewFile();
			}
			OutputStream thumbnailOut = fileBuilder
					.buildFileOutputStream(thumbnailFile);
			if (thumbnailImage instanceof RenderedImage)
			{
				thumbnailToWrite = (RenderedImage)thumbnailImage;
			}
			else
			{
				int width = thumbnailImage.getWidth(null);
				int height = thumbnailImage.getHeight(null);
				BufferedImage bImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
				Graphics bGraphics = bImage.getGraphics();
				bGraphics.drawImage(thumbnailImage, 0, 0, null);
				bGraphics.dispose();
				thumbnailToWrite = bImage;
			}
			ImageIO.write(thumbnailToWrite, "png", thumbnailOut);
			thumbnailOut.close();
		}
	}

	public void duplicateThumbnail(File from, File to)
	{
		InputStream copyFrom = null;
		OutputStream copyTo = null;
		File fromThumb = null;
		File toThumb = null;
		try
		{
			fromThumb = getThumbnailFile(from);
			if (!fromThumb.exists())
			{
				logger.warning("could not duplicate, source thumb does not exist:" + fromThumb);
				return;
			}
			toThumb = getThumbnailFile(to);
			if (!toThumb.exists() && !toThumb.createNewFile())
				throw new IOException("could not create file:" + toThumb);
			copyFrom = fileBuilder.buildFileInputStream(fromThumb);
			copyTo = fileBuilder.buildFileOutputStream(toThumb);
			byte[] readbuf = new byte[8192];
			int numBytesReadIntoReadBuffer = 0;
			while (-1 != (numBytesReadIntoReadBuffer = copyFrom.read(readbuf)))
			{
				copyTo.write(readbuf, 0, numBytesReadIntoReadBuffer);
			}
			// TODO: if thumbnail exists for source, copy for target as well
		}
		catch (IOException ioe)
		{
			logger.severe(String.format("error while copying [%s] to [%s]",	fromThumb, toThumb));
		}
		finally
		{
			try
			{
				copyFrom.close();
			}
			catch (IOException ioe)
			{
			}
			try
			{
				copyTo.close();
			}
			catch (IOException ioe)
			{
			}
		}
	}
}
